package com.lagou.homework.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.jar.Attributes;
import java.util.stream.Collectors;

/**
 * @author boting.guo
 * @date 2021/9/18 23:58
 */

public class AnnotationMetadata {

	private Set<String> annotationTypes;

	private Set<String> mergedAnnotationsTypes;

	private List<Annotation> mergedAnnotations;

	private Map<String, Annotation> annotations;

	public Class<?> getIntrospectedClass() {
		return introspectedClass;
	}

	private Class<?> introspectedClass;

	public static AnnotationMetadata introspect(Class<?> type) {
		AnnotationMetadata annotationMetadata = new AnnotationMetadata();
		annotationMetadata.mergedAnnotations = AnnotationsScanner.getMergedAnnotations(type);
		annotationMetadata.introspectedClass = type;
		return annotationMetadata;
	}

	public Set<String> getAnnotationTypes() {
		if (this.annotationTypes == null) {
			Annotation[] declaredAnnotations = AnnotationsScanner.getDeclaredAnnotations(introspectedClass);
			this.annotationTypes = new HashSet<>(declaredAnnotations.length);
			this.annotations = new HashMap<>(declaredAnnotations.length);

			for (Annotation annotation : declaredAnnotations) {
				String name = annotation.annotationType().getName();
				annotationTypes.add(name);
				annotations.put(name, annotation);
			}
		}
		return this.annotationTypes;
	}

	public Set<String> getMetaAnnotationTypes() {
		if (this.mergedAnnotationsTypes == null) {
			this.mergedAnnotationsTypes = mergedAnnotations.stream()
					.map(annotation -> annotation.getClass().getName())
					.collect(Collectors.toCollection(LinkedHashSet::new));
		}
		return this.mergedAnnotationsTypes;
	}

	public Annotation getAnnotation(String name) {
		return this.annotations.get(name);
	}


	public Properties attributesFor(String type) throws Exception {
		Annotation annotation = this.annotations.get(type);
		if (annotation == null) {
			return null;
		}

		Class<?> clazz = annotation.annotationType();
		Method[] declaredMethods = clazz.getDeclaredMethods();
		Properties attributes = new Properties();

		for (Method method : declaredMethods) {
			String fieldName = method.getName();
			Object fieldValue = method.invoke(annotation);
			attributes.put(fieldName, fieldValue);
		}
		return attributes;
	}
}